package com.spring.boot.core.coder.writers;

import java.io.File;
import java.io.PrintWriter;
import java.util.List;

import com.google.common.collect.Lists;
import com.spring.boot.core.coder.parsers.SqlFileParser;
import com.spring.boot.core.coder.parsers.SqlFileParser.Field;
import com.spring.boot.core.coder.parsers.SqlFileParser.SqlFile;
import com.spring.boot.core.coder.parsers.SqlFileParser.Table;
import com.spring.boot.core.utils.FileUtils;
import com.spring.boot.core.utils.Strings;

public class SqlToServerCode {
	private static final String VERSION = "version";
	private static final String USER_ID = "user_id";

	private static final String MODEL = "model";
	private static final String XML = "xml";
	private static final String MAPPER = "mapper";

	private static String tableClassName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 2);
		for (String s : names) {
			result.append(Strings.firstUpcase(s));
		}
		result.append("DO");
		return result.toString();
	}

	private static String tableKeyClassName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 2);
		for (String s : names) {
			result.append(Strings.firstUpcase(s));
		}
		result.append("DOKey");
		return result.toString();
	}

	private static boolean hasDate(List<Field> fields) {
		for (Field f : fields) {
			if (f.type.equals("Date")) {
				return true;
			}
		}
		return false;
	}

	private static boolean hasUserId(List<Field> fields) {
		for (Field f : fields) {
			if (f.name.equals(USER_ID)) {
				return true;
			}
		}
		return false;
	}

	private static String tableMapperName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 3);
		for (String s : names) {
			result.append(Strings.firstUpcase(s));
		}
		result.append("Map");
		return result.toString();
	}

	private static String fileMapperName(String name) {
		String[] names = name.split("_");
		StringBuffer result = new StringBuffer(name.length() + 6);
		for (String s : names) {
			result.append(Strings.firstUpcase(s));
		}
		result.append("Mapper");
		return result.toString();
	}

	public static void writeTable(Table table, String package_name, PrintWriter mapWriter, String path) throws Exception {
		PrintWriter javaWriter = new PrintWriter(path + tableClassName(table.name) + ".java", "UTF-8");
		mapWriter.printf("    <resultMap id=\"%s\" type=\"%s.%s.%s\" >\n", tableMapperName(table.name), package_name, MODEL, tableClassName(table.name));
		for (Field field : table.fields) {
			if (table.primaryKeysFiled.contains(field)) {
				mapWriter.printf("        <id property=\"%s\" column=\"%s\" />\n", field.javaName, field.name);
			} else {
				mapWriter.printf("        <result property=\"%s\" column=\"%s\" />\n", field.javaName, field.name);
			}

		}
		mapWriter.printf("    </resultMap>\n");
		mapWriter.printf("\n");

		javaWriter.printf("package %s.%s;\n", package_name, MODEL);
		javaWriter.printf("\n");
		if (table.hasDate) {
			javaWriter.printf("import java.util.Date;\n");
		}
		javaWriter.printf("\n");
		javaWriter.printf("public class %s {\n", tableClassName(table.name));
		javaWriter.printf("\n");
		for (Field field : table.fields) {
			javaWriter.printf("    public %s %s;%s\n", field.type, field.javaName, field.comment != null && field.comment.length() > 0 ? "\t// " + field.comment : "");
		}
		javaWriter.printf("\n");
		for (Field field : table.fields) {
			javaWriter.printf("    public %s %s() {\n", field.type, Strings.getter(field.javaName));
			javaWriter.printf("        return %s;\n", field.javaName);
			javaWriter.printf("    }\n");
			javaWriter.printf("\n");

			javaWriter.printf("    public void %s(%s %s) {\n", Strings.setter(field.javaName), field.type, field.javaName);
			javaWriter.printf("        this.%s = %s;\n", field.javaName, field.javaName);
			javaWriter.printf("    }\n");
			javaWriter.printf("\n");
		}
		javaWriter.printf("}\n");
		javaWriter.close();
	}

	public static void writeAtClassDir(String file, String package_name) throws Exception {
		SqlFile sqlFile = SqlFileParser.parse(new File(file));
		String path = FileUtils.getClassDir() + package_name.replace(".", "/") + "/";

		String xmlPath = path + "/" + XML + "/";
		String modelPath = path + "/" + MODEL + "/";
		String mapperPath = path + "/" + MAPPER + "/";

		FileUtils.createDirs(xmlPath);
		FileUtils.createDirs(modelPath);
		FileUtils.createDirs(mapperPath);

		PrintWriter mapXMLWriter = new PrintWriter(xmlPath + fileMapperName(sqlFile.name) + ".xml", "UTF-8");

		mapXMLWriter.printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
		mapXMLWriter.printf("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n");
		mapXMLWriter.printf("\n");
		mapXMLWriter.printf("<mapper namespace=\"%s.%s.%s\">\n", package_name, MAPPER, fileMapperName(sqlFile.name));
		mapXMLWriter.printf("\n");

		String fileName = mapperPath + fileMapperName(sqlFile.name) + ".java";
		PrintWriter mapperWriter = new PrintWriter(fileName, "UTF-8");
		mapperWriter.printf("package %s.%s;\n", package_name, MAPPER);
		mapperWriter.printf("\n");

		mapperWriter.printf("import %s.%s.*;\n", package_name, MODEL);
		mapperWriter.printf("import java.util.List;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Delete;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Insert;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Options;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Param;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.ResultMap;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Select;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Update;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Mapper;\n");

		mapperWriter.printf("\n");
		mapperWriter.printf("@Mapper\n");
		mapperWriter.printf("public interface %s {\n", fileMapperName(sqlFile.name));
		mapperWriter.printf("\n");

		for (Table table : sqlFile.tables) {
			final int fieldSize = table.fields.size();
			String param = Strings.getJavaName(table.name);
			String method = param.substring(0, 1).toUpperCase() + param.substring(1);

			mapXMLWriter.printf("	<!-- %s 映射和相关方法 -->\n", param);
			mapperWriter.printf("	/** %s 映射和相关方法 */\n", param);

			writeTable(table, package_name, mapXMLWriter, modelPath);
			{
				// insert
				mapperWriter.printf("	@Insert(\"insert into %s (", table.name);
				int k = 0;
				for (int i = 0; i < fieldSize; i++) {
					Field field = table.fields.get(i);
					if (table.autoKey == null || !table.autoKey.equals(field.name)) {
						if (k++ > 0) {
							mapperWriter.printf(", ");
						}
						mapperWriter.printf("%s", field.name);
					}
				}
				mapperWriter.printf(") values (");
				k = 0;
				for (int i = 0; i < fieldSize; i++) {
					Field field = table.fields.get(i);
					if (table.autoKey == null || !table.autoKey.equals(field.name)) {
						if (k++ > 0) {
							mapperWriter.printf(", ");
						}
						mapperWriter.printf("#{%s.%s}", param, field.javaName);
					}
				}
				mapperWriter.printf(")\")\n");
				if (table.autoKey != null) {
					mapperWriter.printf("	@Options(useGeneratedKeys = true, keyProperty = \"%s\")\n", param + "." + Strings.getJavaName(table.autoKey));
				}

				mapperWriter.printf("	public int insert%s(@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
				mapperWriter.printf("\n");
			}
			{
				// update
				if (!table.primaryKeysFiled.isEmpty()) {
					mapperWriter.printf("	@Update(\"update %s", table.name + " set ");
					int k = 0;
					for (int i = 0; i < fieldSize; i++) {
						Field field = table.fields.get(i);
						if (!table.primaryKeysFiled.contains(field) && !field.name.equals(VERSION)) {
							if (k++ > 0) {
								mapperWriter.printf(", ");
							}
							mapperWriter.printf("%s = #{%s.%s}", field.name, param, field.javaName);
						}
					}
					if (table.hasVersion) {
						mapperWriter.printf(", version = version + 1 ");
					}
					mapperWriter.printf(" where ");
					k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapperWriter.printf(" and ");
						}
						mapperWriter.printf("%s = #{%s.%s}", field.name, param, field.javaName);
					}
					if (table.hasVersion) {
						mapperWriter.printf(" and version = #{%s.version}", param);
					}
					mapperWriter.printf("\")\n");
					mapperWriter.printf("	public int update%sByPrimaryKey(@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
					mapperWriter.printf("\n");

					if (table.autoKey == null) {
						mapperWriter.printf("	@Update(\"replace into %s(", table.name + "");
						k = 0;
						for (int i = 0; i < fieldSize; i++) {
							Field field = table.fields.get(i);
							if (k++ > 0) {
								mapperWriter.printf(", ");
							}
							mapperWriter.printf("%s", field.name);
						}
						mapperWriter.printf(") values (");
						k = 0;
						for (int i = 0; i < fieldSize; i++) {
							Field field = table.fields.get(i);
							if (k++ > 0) {
								mapperWriter.printf(", ");
							}
							mapperWriter.printf("#{%s.%s}", param, field.javaName);
						}
						mapperWriter.printf(")\")\n");
						mapperWriter.printf("	public int set%s(@Param(\"%s\") %s %s);\n", method, param, tableClassName(table.name), param);
						mapperWriter.printf("\n");
					}
				}
			}
			{
				// select
				if (table.hasUserId) {
					if (hasUserId(table.primaryKeysFiled) && table.primaryKeysFiled.size() == 1) {
						mapperWriter.printf("	@Select(\"select * from %s where user_id = #{userId}\")\n", table.name);
						mapperWriter.printf("	@ResultMap(\"%s.%s.%s.%s\")\n", package_name, MAPPER, fileMapperName(sqlFile.name), tableMapperName(table.name));

						mapperWriter.printf("	public %s get%s (@Param(\"userId\") Long userId );\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					} else {
						mapperWriter.printf("	@Select(\"select * from %s where user_id = #{userId}\")\n", table.name);
						mapperWriter.printf("	@ResultMap(\"%s.%s.%s.%s\")\n", package_name, MAPPER, fileMapperName(sqlFile.name), tableMapperName(table.name));

						mapperWriter.printf("	public List<%s> get%ss (@Param(\"userId\") Long userId );\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					}
				}
				if (table.primaryKeysFiled.size() == 1 && !hasUserId(table.primaryKeysFiled)) {
					Field field = table.primaryKeysFiled.get(0);
					mapperWriter.printf("	@Select(\"select * from %s where %s = #{%s}\")\n", table.name, field.name, field.javaName);
					mapperWriter.printf("	@ResultMap(\"%s.%s.%s.%s\")\n", package_name, MAPPER, fileMapperName(sqlFile.name), tableMapperName(table.name));
					mapperWriter.printf("	public %s select%sByPrimaryKey(@Param(\"%s\") %s %s);\n", tableClassName(table.name), method, field.javaName, field.type, field.javaName);
					mapperWriter.printf("\n");
				} else if (table.primaryKeysFiled.size() > 1) {
					mapperWriter.printf("	@Select(\"select * from %s", table.name);
					mapperWriter.printf(" where ");
					int k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapperWriter.printf(" and ");
						}
						mapperWriter.printf("%s = #{#s}", field.name, field.javaName);
					}
					mapperWriter.printf("\")\n");
					mapperWriter.printf("	@ResultMap(\"%s.%s.%s.%s\")\n", package_name, MAPPER, fileMapperName(sqlFile.name), tableMapperName(table.name));
					mapperWriter.printf("	public %s select%sByPrimaryKey(", tableClassName(table.name), method);
					k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapperWriter.printf(", ");
						}
						mapperWriter.printf("@Param(\"%s\") %s %s", field.javaName, field.type, field.javaName);

					}
					mapperWriter.printf(");\n");
					mapperWriter.printf("\n");
				}
			}
			{
				if (!table.primaryKeysFiled.isEmpty()) {
					mapperWriter.printf("	@Delete(\"delete from %s where ", table.name);
					int k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapperWriter.printf(" and ");
						}
						mapperWriter.printf(field.name + " = #{" + field.javaName + "}");
					}
					mapperWriter.printf("\")\n");
					mapperWriter.printf("	public int delete%sByPrimaryKey(", method);
					k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapperWriter.printf(", ");
						}
						mapperWriter.printf("@Param(\"%s\") %s %s", field.javaName, field.type, field.javaName);
					}
					mapperWriter.printf(");\n");
					mapperWriter.printf("\n");
				}
			}
		}
		mapXMLWriter.printf("</mapper>\n");
		mapXMLWriter.close();
		mapperWriter.printf("}\n");
		mapperWriter.close();
	}

	/**
	 * 生产基于xml映射文件的sql
	 * 
	 * @param file
	 * @param package_name
	 * @throws Exception
	 */
	public static void writeAtClassDirXML(String file, String package_name) throws Exception {
		SqlFile sqlFile = SqlFileParser.parse(new File(file));
		String path = FileUtils.getClassDir() + package_name.replace(".", "/") + "/";

		String xmlPath = path + "/" + XML + "/";
		String modelPath = path + "/" + MODEL + "/";
		String mapperPath = path + "/" + MAPPER + "/";

		FileUtils.createDirs(xmlPath);
		FileUtils.createDirs(modelPath);
		FileUtils.createDirs(mapperPath);

		PrintWriter mapXMLWriter = new PrintWriter(xmlPath + fileMapperName(sqlFile.name) + ".xml", "UTF-8");
		mapXMLWriter.printf("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
		mapXMLWriter.printf("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n");
		mapXMLWriter.printf("\n");
		mapXMLWriter.printf("<mapper namespace=\"%s.%s.%s\">\n", package_name, MAPPER, fileMapperName(sqlFile.name));
		mapXMLWriter.printf("\n");

		String fileName = mapperPath + fileMapperName(sqlFile.name) + ".java";
		PrintWriter mapperWriter = new PrintWriter(fileName, "UTF-8");
		mapperWriter.printf("package %s.%s;\n", package_name, MAPPER);
		mapperWriter.printf("\n");

		mapperWriter.printf("import %s.%s.*;\n", package_name, MODEL);
		mapperWriter.printf("import java.util.List;\n");
		mapperWriter.printf("import org.apache.ibatis.annotations.Mapper;\n");

		mapperWriter.printf("\n");
		mapperWriter.printf("@Mapper\n");
		mapperWriter.printf("public interface %s {\n", fileMapperName(sqlFile.name));
		mapperWriter.printf("\n");

		for (Table table : sqlFile.tables) {

			final int fieldSize = table.fields.size();
			String param = Strings.getJavaName(table.name);
			String method = param.substring(0, 1).toUpperCase() + param.substring(1);

			mapXMLWriter.printf("	<!-- %s 映射和相关方法 -->\n", param);
			mapperWriter.printf("	/** %s 映射和相关方法 */\n", param);
			writeTableXML(table, package_name, mapXMLWriter, modelPath);
			{
				// insert
				mapXMLWriter.printf("	<insert id=\"insert%s\" parameterType=\"%s.%s.%s\"", method, package_name, MODEL, tableClassName(table.name));
				if (table.autoKey != null) {
					mapXMLWriter.printf(" useGeneratedKeys=\"true\" keyProperty=\"%s\">\n", Strings.getJavaName(table.autoKey));
				} else {
					mapXMLWriter.printf(">\n");
				}
				mapXMLWriter.printf("		insert into %s\n", table.name);
				mapXMLWriter.printf("		<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\" >\n");
				for (int i = 0; i < fieldSize; i++) {
					Field field = table.fields.get(i);
					if (table.autoKey == null || !table.autoKey.equals(field.name)) {
						mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
						mapXMLWriter.printf("				%s,\n", field.name);
						mapXMLWriter.printf("			</if>\n");
					}
				}
				mapXMLWriter.printf("		</trim>\n");
				mapXMLWriter.printf("		<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\" >\n");

				for (int i = 0; i < fieldSize; i++) {
					Field field = table.fields.get(i);
					if (table.autoKey == null || !table.autoKey.equals(field.name)) {
						mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
						mapXMLWriter.printf("				#{%s},\n", field.javaName);
						mapXMLWriter.printf("			</if>\n");
					}
				}
				mapXMLWriter.printf("		</trim>\n");
				mapXMLWriter.printf("	</insert>\n");
				mapXMLWriter.printf("\n");

				mapperWriter.printf("	public int insert%s(%s %s);\n", method, tableClassName(table.name), param);
				mapperWriter.printf("\n");
			}
			{
				// update
				if (!table.primaryKeysFiled.isEmpty()) {
					mapXMLWriter.printf("	<update id=\"update%sByPrimaryKey\" parameterType=\"%s.%s.%s\">\n", method, package_name, MODEL, tableClassName(table.name));
					mapXMLWriter.printf("		update %s\n", table.name);
					mapXMLWriter.printf("		<set>\n");
					for (int i = 0; i < fieldSize; i++) {
						Field field = table.fields.get(i);
						if (!table.primaryKeysFiled.contains(field) && !field.name.equals(VERSION)) {
							mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
							mapXMLWriter.printf("				%s = #{%s},\n", field.name, field.javaName);
							mapXMLWriter.printf("			</if>\n");
						}
					}
					if (table.hasVersion) {
						mapXMLWriter.printf("			version = version + 1 ");
						mapXMLWriter.printf("\n");
					}
					mapXMLWriter.printf("		</set>\n");
					mapXMLWriter.printf(" 		where ");
					int k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapXMLWriter.printf(" and ");
						}
						mapXMLWriter.printf(field.name + " = #{" + field.javaName + "}");
					}
					if (table.hasVersion) {
						mapXMLWriter.printf(" and version" + " = #{version}");
					}
					mapXMLWriter.printf("\n");
					mapXMLWriter.printf("	</update>\n");
					mapXMLWriter.printf("\n");

					mapperWriter.printf("	public int update%sByPrimaryKey(%s %s);\n", method, tableClassName(table.name), param);
					mapperWriter.printf("\n");

					// replace
					if (table.autoKey == null) {
						mapXMLWriter.printf("	<update id=\"set%s\" parameterType=\"%s.%s.%s\">\n", method, package_name, MODEL, tableClassName(table.name));

						mapXMLWriter.printf("		replace into %s\n", table.name);
						mapXMLWriter.printf("		<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\" >\n");
						for (int i = 0; i < fieldSize; i++) {
							Field field = table.fields.get(i);
							mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
							mapXMLWriter.printf("				%s,\n", field.name);
							mapXMLWriter.printf("			</if>\n");
						}
						mapXMLWriter.printf("		</trim>\n");
						mapXMLWriter.printf("		<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\" >\n");
						for (int i = 0; i < fieldSize; i++) {
							Field field = table.fields.get(i);
							mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
							mapXMLWriter.printf("				#{%s},\n", field.javaName);
							mapXMLWriter.printf("			</if>\n");
						}
						mapXMLWriter.printf("		</trim>\n");
						mapXMLWriter.printf("	</update>\n");
						mapXMLWriter.printf("\n");

						mapperWriter.printf("	public int set%s(%s %s);\n", method, tableClassName(table.name), param);
						mapperWriter.printf("\n");
					}
				}
			}
			{
				// select[如果有userId]
				if (table.hasUserId) {
					if (hasUserId(table.primaryKeysFiled) && table.primaryKeysFiled.size() == 1) {
						mapXMLWriter.printf("	<select id=\"get%s\" parameterType=\"java.lang.%s\" resultMap=\"%s\">\n", method, "Long", tableMapperName(table.name));
						mapXMLWriter.printf("		select * from %s\n", table.name);
						mapXMLWriter.printf("		where user_id = #{userId}\n");
						mapXMLWriter.printf("	</select>\n");
						mapXMLWriter.printf("\n");

						mapperWriter.printf("	public %s get%s(Long userId );\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					} else {
						mapXMLWriter.printf("	<select id=\"get%ss\" parameterType=\"java.lang.%s\" resultMap=\"%s\">\n", method, "Long", tableMapperName(table.name));
						mapXMLWriter.printf("		select * from %s\n", table.name);
						mapXMLWriter.printf("		where user_id = #{userId}\n");
						mapXMLWriter.printf("	</select>\n");
						mapXMLWriter.printf("\n");

						mapperWriter.printf("	public List<%s> get%ss(Long userId);\n", tableClassName(table.name), method);
						mapperWriter.printf("\n");
					}
				}
				// select[new add]
				if (table.primaryKeysFiled.size() == 1 && !hasUserId(table.primaryKeysFiled)) {
					Field field = table.primaryKeysFiled.get(0);
					mapXMLWriter.printf("	<select id=\"select%sByPrimaryKey\" parameterType=\"java.lang.%s\" resultMap=\"%s\">\n", method, field.type, tableMapperName(table.name));
					mapXMLWriter.printf("		select * from %s\n", table.name);
					mapXMLWriter.printf("		where %s = #{%s}", field.name, field.javaName);
					mapXMLWriter.printf("\n");
					mapXMLWriter.printf("	</select>\n");
					mapXMLWriter.printf("\n");

					mapperWriter.printf("	public %s select%sByPrimaryKey(%s %s);\n", tableClassName(table.name), method, field.type, field.javaName);
					mapperWriter.printf("\n");
				} else if (table.primaryKeysFiled.size() > 1) {
					mapXMLWriter.printf("	<select id=\"select%sByPrimaryKey\" parameterType=\"%s.%s.%s\" resultMap=\"%s\">\n", method, package_name, MODEL, tableKeyClassName(table.name), tableMapperName(table.name));
					mapXMLWriter.printf("		select * from %s\n", table.name);
					mapXMLWriter.printf("		where ");
					int k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapXMLWriter.printf(" and ");
						}
						mapXMLWriter.printf("%s = #{%s}", field.name, field.javaName);
					}
					mapXMLWriter.printf("\n");
					mapXMLWriter.printf("	</select>\n");
					mapXMLWriter.printf("\n");

					mapperWriter.printf("	public %s select%sByPrimaryKey(%s %s);\n", tableClassName(table.name), method, tableKeyClassName(table.name), param);
					mapperWriter.printf("\n");
				}

				mapXMLWriter.printf("	<select id=\"select%ss\" parameterType=\"%s.%s.%s\" resultMap=\"%s\">\n", method, package_name, MODEL, tableClassName(table.name), tableMapperName(table.name));
				mapXMLWriter.printf("		select * from %s where 1=1 \n", table.name);
				mapXMLWriter.printf("		<trim prefix=\"\" suffixOverrides=\",\" >\n");

				for (int i = 0; i < fieldSize; i++) {
					Field field = table.fields.get(i);
					mapXMLWriter.printf("			<if test=\"%s != null\">\n", field.javaName);
					mapXMLWriter.printf("				and %s = #{%s},\n", field.name, field.javaName);
					mapXMLWriter.printf("			</if>\n");
				}
				mapXMLWriter.printf("		</trim>\n");
				mapXMLWriter.printf("	</select>\n");
				mapXMLWriter.printf("\n");

				mapperWriter.printf("	public List<%s> select%ss(%s %s);\n", tableClassName(table.name), method, tableClassName(table.name), param);
				mapperWriter.printf("\n");
			}
			{
				// delete
				if (table.primaryKeysFiled.size() > 1) {
					mapXMLWriter.printf("	<delete id=\"delete%sByPrimaryKey\" parameterType=\"%s.%s.%s\">\n", method, package_name, MODEL, tableKeyClassName(table.name));
					mapXMLWriter.printf("		delete from %s\n", table.name);
					mapXMLWriter.printf("		where ");
					int k = 0;
					for (Field field : table.primaryKeysFiled) {
						if (k++ > 0) {
							mapXMLWriter.printf(" and ");
						}
						mapXMLWriter.printf("%s = #{%s}", field.name, field.javaName);
					}
					mapXMLWriter.printf("\n");
					mapXMLWriter.printf("	</delete>\n");
					mapXMLWriter.printf("\n");

					mapperWriter.printf("	public int delete%sByPrimaryKey(%s %s);\n", method, tableKeyClassName(table.name), param);
					mapperWriter.printf("\n");
				} else if (table.primaryKeysFiled.size() == 1) {
					Field field = table.primaryKeysFiled.get(0);
					mapXMLWriter.printf("	<delete id=\"delete%sByPrimaryKey\" parameterType=\"java.lang.%s\">\n", method, field.type);
					mapXMLWriter.printf("		delete from %s\n", table.name);
					mapXMLWriter.printf("		where %s = #{%s}", field.name, field.javaName);
					mapXMLWriter.printf("\n");
					mapXMLWriter.printf("	</delete>\n");
					mapXMLWriter.printf("\n");

					mapperWriter.printf("	public int delete%sByPrimaryKey(%s %s);\n", method, field.type, field.javaName);
					mapperWriter.printf("\n");
				}
			}
		}
		mapXMLWriter.printf("</mapper>\n");
		mapXMLWriter.close();
		mapperWriter.printf("}\n");
		mapperWriter.close();
	}

	/**
	 * 生成xml的 table
	 * 
	 * @param table
	 * @param package_name
	 * @param mapWriter
	 * @param modelPath
	 * @throws Exception
	 */
	public static void writeTableXML(Table table, String package_name, PrintWriter mapWriter, String path) throws Exception {
		mapWriter.printf("    <resultMap id=\"%s\" type=\"%s.%s.%s\" >\n", tableMapperName(table.name), package_name, MODEL, tableClassName(table.name));
		for (Field field : table.fields) {
			if (table.primaryKeysFiled.contains(field)) {
				mapWriter.printf("        <id property=\"%s\" column=\"%s\" />\n", field.javaName, field.name);
			} else {
				mapWriter.printf("        <result property=\"%s\" column=\"%s\" />\n", field.javaName, field.name);
			}
		}
		mapWriter.printf("    </resultMap>\n");
		mapWriter.printf("\n");

		// // 2个主键
		if (table.primaryKeysFiled.size() > 1) {
			// 生成父类
			PrintWriter javaWriter = new PrintWriter(path + tableKeyClassName(table.name) + ".java", "UTF-8");
			javaWriter.printf("package %s.%s;\n", package_name, MODEL);
			javaWriter.printf("\n");
			if (hasDate(table.primaryKeysFiled)) {
				javaWriter.printf("import java.util.Date;\n");
			}
			javaWriter.printf("\n");
			javaWriter.printf("public class %s {\n", tableKeyClassName(table.name));
			javaWriter.printf("\n");
			for (Field field : table.primaryKeysFiled) {
				javaWriter.printf("	public %s %s;%s\n", field.type, field.javaName, field.comment != null && field.comment.length() > 0 ? "\t// " + field.comment : "");
			}
			javaWriter.printf("\n");
			for (Field field : table.primaryKeysFiled) {
				javaWriter.printf("	public %s %s() {\n", field.type, Strings.getter(field.javaName));
				javaWriter.printf("		return %s;\n", field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");

				javaWriter.printf("	public void %s(%s %s) {\n", Strings.setter(field.javaName), field.type, field.javaName);
				javaWriter.printf("		this.%s = %s;\n", field.javaName, field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");
			}
			javaWriter.printf("}\n");
			javaWriter.close();
			// 子类
			javaWriter = new PrintWriter(path + tableClassName(table.name) + ".java", "UTF-8");
			javaWriter.printf("package %s.%s;\n", package_name, MODEL);
			javaWriter.printf("\n");
			if (hasDate(table.fields)) {
				javaWriter.printf("import java.util.Date;\n");
			}
			javaWriter.printf("\n");
			javaWriter.printf("public class %s extends %s{\n", tableClassName(table.name), tableKeyClassName(table.name));
			javaWriter.printf("\n");
			for (Field field : table.fields) {
				if (table.primaryKeysFiled.contains(field)) {
					continue;
				}
				javaWriter.printf("	public %s %s;%s\n", field.type, field.javaName, field.comment != null && field.comment.length() > 0 ? "\t// " + field.comment : "");
			}
			javaWriter.printf("\n");
			for (Field field : table.fields) {
				if (table.primaryKeysFiled.contains(field)) {
					continue;
				}
				javaWriter.printf("	public %s %s() {\n", field.type, Strings.getter(field.name));
				javaWriter.printf("		return %s;\n", field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");

				javaWriter.printf("	public void %s(%s %s) {\n", Strings.setter(field.javaName), field.type, field.javaName);
				javaWriter.printf("		this.%s = %s;\n", field.javaName, field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");
			}
			javaWriter.printf("}\n");
			javaWriter.close();
		} else {
			PrintWriter javaWriter = new PrintWriter(path + tableClassName(table.name) + ".java", "UTF-8");
			javaWriter.printf("package %s.%s;\n", package_name, MODEL);
			javaWriter.printf("\n");
			if (table.hasDate) {
				javaWriter.printf("import java.util.Date;\n");
			}
			javaWriter.printf("\n");
			javaWriter.printf("public class %s {\n", tableClassName(table.name));
			javaWriter.printf("\n");
			for (Field field : table.fields) {
				javaWriter.printf("	public %s %s;%s\n", field.type, field.javaName, field.comment != null && field.comment.length() > 0 ? "\t// " + field.comment : "");
			}
			javaWriter.printf("\n");
			for (Field field : table.fields) {
				javaWriter.printf("	public %s %s() {\n", field.type, Strings.getter(field.javaName));
				javaWriter.printf("		return %s;\n", field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");

				javaWriter.printf("	public void %s(%s %s) {\n", Strings.setter(field.javaName), field.type, field.javaName);
				javaWriter.printf("		this.%s = %s;\n", field.javaName, field.javaName);
				javaWriter.printf("	}\n");
				javaWriter.printf("\n");
			}
			javaWriter.printf("}\n");
			javaWriter.close();
		}
	}

	public static void copyMappers(String searchDir, String toDir) throws Exception {
		FileUtils.createDirs(toDir);
		List<File> xmlFiles = Lists.newArrayList();
		FileUtils.searchFile(new File(searchDir), ".xml", xmlFiles);
		for (File file : xmlFiles) {
			if (file.getName().endsWith("Mapper.xml")) {
				FileUtils.copyFile(file, toDir + file.getName());
			}
			file.delete();
		}
	}

	public static void deleteMappers(String searchDir) throws Exception {
		List<File> xmlFiles = Lists.newArrayList();
		FileUtils.searchFile(new File(searchDir), ".java", xmlFiles);
		for (File file : xmlFiles) {
			if (file.getName().endsWith("Mapper.java")) {
				file.delete();
			}
		}
	}
}
